import java.util.Arrays;

public class PLLayerPattern extends PLLayer {
	PLPattern[] mPatterns;
	PLMNDist mShapeDists = null;
	
	final static int kMaxPatternstoCheck = 5;
	final static float kMaxPatternWeighttoCheck = 0.4f;
	final static float kMinAccepted = 0.3f;
	
	public PLLayerPattern() {
	}
	
	public PLLayerPattern(String name) {	
		super(name);
		Init();
	}

	protected void finalize() throws Throwable {
		Shutdown();
	  //do finalization here
	  super.finalize(); //not necessary if extending Object.
	} 
	
	public boolean Init() {
		if (!InitDataFromFile()) 
			return false;
		return true;
	}

	public boolean Shutdown() {
		return SaveDataToFile();
	}

	public int NumPatterns() {return mPatterns.length;}

	// a layer may be asked to build a response to a query from scratch.
	// returns a new shape as a response and the confidence in this response
	public float BuildResponse(PLShape query, PLOutShape  outResponse) {
		// use the response where most patterns agree
		return 0;
	}	

	// given a query, response pair a layer may be asked to validate the response 
	// and possible return a better match if the confidence in the given response is not high
	public  float ValidateResponse(PLShape query, PLShape response, PLOutShape  outBetterMatch) {	
		if (!query.IsValid() || !response.IsValid()) 
			return 0f;


		PLDist dist = GetRightDistribution(query); 
		
		int[] indices = dist.GetDominantIndicesWithFractionWeight(kMaxPatternWeighttoCheck);

		if (indices == null) {
			indices = dist.GetKDominantIndices(kMaxPatternstoCheck);			
		}

		if (indices == null) {
			PLDebug.Warn("No indices for pattern. Num registerd = %d", NumPatterns());						
			return 0;
		}		

		// use the response where most patterns agree		
		float confidence = 0;
		
		float[] results = new float[indices.length];
		PLOutShape[] betterShapes = new PLOutShape[indices.length];
		for (int i =0; i < indices.length; ++i) {			
			results[i] = mPatterns[indices[i]].ValidateResponse(query, response, betterShapes[i]);
		}

		// the max value is the normalizer for the results
		float normalizer = dist.GetValueAt(indices[0]); 

		float best_result = 0;
		float actual_result = 0;
		for (int i =0; i < indices.length; ++i) {			
			float factor = dist.GetValueAt(indices[i])/normalizer;
			best_result += factor;			

			results[i] *=  factor;			
			actual_result += results[i]; 
		}

		confidence = actual_result/best_result;
		// if the results of validation are acceptable, return
		if (confidence >= kMinAccepted) 
			return confidence;
		
		// we must find a better match now;
		// some kind of a weighted average of shapes or quorum of results, not sure.
		
		
		// test with all k patterns
		// if all is well and good return true 
		// else find some kind of average or something and return this
		
		return confidence;
	}			


	@Override
	public float ImproveResponse(PLShape query, PLOutShape outResponse, PLShape  betterMatch) {
		// TODO Auto-generated method stub
		return 0;
	}

	// PRIVATE MEMBERS

	// inits the patterns from the file
	private boolean InitDataFromFile() {

		// first check if modified file exists, otherwise the base file
		String filename = PLFileUtils.GetModifiedPatternFile(GetName());
		if (!PLFileUtils.Exists(filename)) {
			filename = PLFileUtils.GetPatternFile(GetName());
			if (!PLFileUtils.Exists(filename)) {
				PLDebug.Err("Pattern file does not exist");
				return false;
			}
		}
						
		// get the key value file name
		PLFileKeyValue<PLMNDist, String[]> file = new PLFileKeyValue<PLMNDist, String[]>(filename);
		file.BeginRead();

		// there must be one record in the file
		if (file.GetNumOfRecords() < 1) {
			return false;
		}

		PLRecordKeyValue<PLMNDist, String[]> record = file.GetNextRecord();
		
		mShapeDists = record.GetKey();
		String[] patternNames = record.GetValue();
				
		mPatterns = new PLPattern[patternNames.length];

		// read each record and create the corresponding pattern for it.
		int count =0;
		for (int i = 0; i < patternNames.length; ++i) {			
			mPatterns[count] = PLPattern.GetPatternByName(patternNames[i]);
			if (mPatterns[count] != null) {				
				count++;
			} else {
				PLDebug.Warn("Pattern %s could not be found", patternNames[i]);
			}
		}

		// no pattern found
		if (count == 0) {
			PLDebug.Warn("No pattern found, %d Patterns tried", patternNames.length);
			return false;
		}

		// resize to get right size
		if (count < patternNames.length) {
			mPatterns = Arrays.copyOf(mPatterns, count);
		}
								
		// all done 	
		return true;
	}

	private boolean SaveDataToFile() {

		// first check if modified file exists, otherwise the base file
		String filename = PLFileUtils.GetModifiedPatternFile(GetName());

		String[] names = new String[NumPatterns()];
		for (int i =0; i < NumPatterns(); ++i) {
			names[i] = mPatterns[i].GetName();
 		}

		PLRecordKeyValue<PLMNDist, String[]> record = new PLRecordKeyValue<PLMNDist, String[]>();		
		record.Set(mShapeDists, names); 	
						
		// get the key value file name
		PLFileKeyValue<PLMNDist, String[]> file = new PLFileKeyValue<PLMNDist, String[]>(filename);
		file.WriteRecord(record);		

		file.FinishedWriting();
												
		// all done 	
		return true;
	}

	private PLDist GetRightDistribution(PLShape query) {
		// check if this shape has no distribution, in that case return the average distribution and hope for the best
		if (query.GetShapeId().getId() >= mShapeDists.GetSize()) {
			return mShapeDists.GetAverageDist();
		}
		// get the shape of the query and the equivalent distribution
		return mShapeDists.GetDistributionAt(query.GetShapeId().getId());
	}	
}
